package com.meeruu.sg.account.core.domain.aspect;

import com.meeruu.common.utils.LoggerUtil;
import com.meeruu.sg.account.core.api.constant.AccountType;
import com.meeruu.sg.account.core.api.constant.FundsOprType;
import com.meeruu.sg.account.core.api.constant.PlatformType;
import com.meeruu.sg.account.core.api.constant.RequestFlowStatus;
import com.meeruu.sg.account.core.api.dto.request.fund.*;
import com.meeruu.sg.account.core.api.exception.AccountException;
import com.meeruu.sg.account.core.api.exception.ErrorCode;
import com.meeruu.sg.account.core.domain.biz.AccountCacheBiz;
import com.meeruu.sg.account.core.domain.model.FundRequestRecordDO;
import com.meeruu.sg.account.core.domain.service.FundRequestRecordService;
import com.meeruu.sg.account.core.domain.vo.UnFrozenDetailRequest;
import com.meeruu.sg.account.core.domain.vo.UnFrozenToOtherDetailRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * @author yangqintao
 * @date 2019-05-22
 * @description: 账户交互请求流水落地aop
 */
@Aspect
@Configuration
@Slf4j
public class RequestFlowAOP {


    @Autowired
    private FundRequestRecordService fundRequestRecordService;

    @Autowired
    private AccountCacheBiz accountCacheBiz;


    @Around("@annotation(com.meeruu.sg.account.core.domain.aspect.RequestSink)")
    public Object invoke(ProceedingJoinPoint point) throws Throwable {
        /**
         * 1. 执行方法前数据先落地
         * 2. 执行方法后更新状态
         */
        Object[] args = point.getArgs();
        BaseRequest request = (BaseRequest) args[0];

        // outBizNo校验 当前outBizNo存在成功/进行中时,抛出异常
        this.validateRecord(request);

        try {
            String flowId = (String) point.proceed(args);

            // 插入成功record
            FundRequestRecordDO fundRequestRecord = this.getRequestFlow(request);
            fundRequestRecord.setFlowId(flowId);
            fundRequestRecord.setStatus(RequestFlowStatus.SUCCESS.getCode());
            fundRequestRecordService.insert(fundRequestRecord);

            // 账户缓存清理
            this.invokeCleanCache(request);

            return flowId;
        } catch (Exception e) {
            LoggerUtil.error(log, "业务执行异常", e);

            // 插入失败record
            FundRequestRecordDO fundRequestRecord = this.getRequestFlow(request);
            fundRequestRecord.setStatus(RequestFlowStatus.FAIL.getCode());
            fundRequestRecord.setExtendInfo(StringUtils.substring(e.getMessage(), 0, 63));
            fundRequestRecordService.insert(fundRequestRecord);
            throw e;
        }
    }

    /**
     * outBizNo校验 当前outBizNo存在成功/进行中时,抛出异常
     */
    private void validateRecord(BaseRequest request) {
        List<Integer> statusGroup = new ArrayList<>();
        statusGroup.add(RequestFlowStatus.SUCCESS.getCode());
        statusGroup.add(RequestFlowStatus.RUNNING.getCode());
        List<FundRequestRecordDO> queryList = fundRequestRecordService.selectByOutBizNoAndPlatformType(request.getOutBizNo(), getPlatformType(request), statusGroup);

        if (CollectionUtils.isNotEmpty(queryList)) {
            LoggerUtil.error(log, "请求流水正在处理或处理成功, 交易流水号: {}", request.getOutBizNo());
            throw new AccountException(ErrorCode.BIZ_REQUEST_FLOW_EXIST_ERROR);
        }
    }


    /**
     * 记录处理中状态请求流水
     */
    private FundRequestRecordDO getRequestFlow(BaseRequest request) {
        String outBizNo = request.getOutBizNo();
        int platformType = getPlatformType(request);

        FundRequestRecordDO fundRequestRecord = FundRequestRecordDO.builder()
                .outBizNo(outBizNo)
                .platformType(platformType)
                .applyDate(request.getApplyDate())
                .status(RequestFlowStatus.RUNNING.getCode())
                .build();

        if (request instanceof ChargeRequest) {
            // 充值
            ChargeRequest chargeRequest = (ChargeRequest) request;
            fundRequestRecord.setAccountId(chargeRequest.getAccountId());
            fundRequestRecord.setAccountType(chargeRequest.getAccountType().getCode());
            fundRequestRecord.setFundType(FundsOprType.RECHARGE.getCode());
            fundRequestRecord.setAmount(chargeRequest.getChargeAmount());

        } else if (request instanceof FundTransRequest) {
            // 转账
            FundTransRequest fundTransRequest = (FundTransRequest) request;
            fundRequestRecord.setAccountId(fundTransRequest.getOriginAccountId());
            fundRequestRecord.setAccountType(fundTransRequest.getOriginAccountType().getCode());
            fundRequestRecord.setFundType(FundsOprType.TRANSFER.getCode());
            fundRequestRecord.setTargetAccountId(fundTransRequest.getTargetAccountId());
            fundRequestRecord.setAmount(fundTransRequest.getTransAmount());

        } else if (request instanceof FrozenRequest) {
            // 冻结
            FrozenRequest frozenRequest = (FrozenRequest) request;
            fundRequestRecord.setAccountId(frozenRequest.getAccountId());
            fundRequestRecord.setAccountType(frozenRequest.getAccountType().getCode());
            fundRequestRecord.setFundType(FundsOprType.FROZEN.getCode());
            fundRequestRecord.setAmount(frozenRequest.getAmount());

        } else if (request instanceof UnFrozenDetailRequest) {
            // 逆向解冻
            UnFrozenDetailRequest unFrozenRequest = (UnFrozenDetailRequest) request;
            fundRequestRecord.setAccountId(unFrozenRequest.getAccountId());
            fundRequestRecord.setAccountType(unFrozenRequest.getAccountType().getCode());
            fundRequestRecord.setFundType(FundsOprType.UN_FROZEN.getCode());
            fundRequestRecord.setAmount(unFrozenRequest.getAmount());

        } else if (request instanceof UnFrozenToOtherDetailRequest) {
            // 正向解冻
            UnFrozenToOtherDetailRequest unFrozenToOtherRequest = (UnFrozenToOtherDetailRequest) request;
            fundRequestRecord.setAccountId(unFrozenToOtherRequest.getAccountId());
            fundRequestRecord.setAccountType(unFrozenToOtherRequest.getAccountType().getCode());
            fundRequestRecord.setFundType(FundsOprType.UN_FROZEN_TO_OTHER.getCode());
            fundRequestRecord.setAmount(unFrozenToOtherRequest.getAmount());
            fundRequestRecord.setTargetAccountId(unFrozenToOtherRequest.getTargetAccountId());

        } else if (request instanceof FundDelayInTransRequest) {
            // 入账户延时转账
            FundDelayInTransRequest fundTransRequest = (FundDelayInTransRequest) request;
            fundRequestRecord.setAccountId(fundTransRequest.getOriginAccountId());
            fundRequestRecord.setAccountType(fundTransRequest.getOriginAccountType().getCode());
            fundRequestRecord.setFundType(FundsOprType.DELAY_TRANSFER.getCode());
            fundRequestRecord.setTargetAccountId(fundTransRequest.getTargetAccountId());
            fundRequestRecord.setAmount(fundTransRequest.getTransAmount());

        }

        return fundRequestRecord;
    }


    private Integer getPlatformType(BaseRequest request) {
        return request.getPlatformType() == null ? PlatformType.SG.getCode() : request.getPlatformType().getCode();
    }


    /**
     * 缓存清理
     */
    private void invokeCleanCache(BaseRequest request) {
        if (request instanceof ChargeRequest) {
            // 充值
            ChargeRequest chargeRequest = (ChargeRequest) request;
            accountCacheBiz.cleanCache(chargeRequest.getAccountId(), chargeRequest.getAccountGroup(), chargeRequest.getAccountType());

        } else if (request instanceof FundTransRequest) {
            // 转账
            FundTransRequest fundTransRequest = (FundTransRequest) request;

            accountCacheBiz.cleanCache(fundTransRequest.getOriginAccountId(),
                    fundTransRequest.getOriginAccountGroup(), fundTransRequest.getOriginAccountType());
            accountCacheBiz.cleanCache(fundTransRequest.getTargetAccountId(),
                    fundTransRequest.getTargetAccountGroup(), fundTransRequest.getTargetAccountType());

        } else if (request instanceof FrozenRequest) {
            // 冻结
            FrozenRequest frozenRequest = (FrozenRequest) request;
            accountCacheBiz.cleanCache(frozenRequest.getAccountId(),
                    frozenRequest.getAccountGroup(), frozenRequest.getAccountType());

        } else if (request instanceof UnFrozenDetailRequest) {
            // 逆向解冻
            UnFrozenDetailRequest unFrozenRequest = (UnFrozenDetailRequest) request;
            accountCacheBiz.cleanCache(unFrozenRequest.getAccountId(),
                    unFrozenRequest.getAccountGroup(), unFrozenRequest.getAccountType());

        } else if (request instanceof UnFrozenToOtherDetailRequest) {
            // 正向解冻
            UnFrozenToOtherDetailRequest unFrozenToOtherRequest = (UnFrozenToOtherDetailRequest) request;
            accountCacheBiz.cleanCache(unFrozenToOtherRequest.getAccountId(),
                    unFrozenToOtherRequest.getAccountGroup(), unFrozenToOtherRequest.getAccountType());

            accountCacheBiz.cleanCache(unFrozenToOtherRequest.getTargetAccountId(),
                    unFrozenToOtherRequest.getTargetAccountGroup(), unFrozenToOtherRequest.getAccountType());
        }
    }

}
